# $Id: backup.py 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
import os
import shutil
import Queue

from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings

from djanet.lib import ip, stdlib, log
from djanet.compartment.models import ConfigBackup
from djanet.db.netdb import Device, Ipblock
from djanet.lib.exception import AppError, IPError
from djanet.tools.lib import device

class ConfigBackup(object):
   def __init__(self, Host = [], Sleep = 0):
      super(ConfigBackup, self).__init__()
      self._log = log.StreamLog(stdlib.my_name(self))
      self._err = AppError()
      self._svn = None
      self._devList = []
      self._host = []
      self._q = Queue.Queue()
      self._svndir = None
      self._count = 0

      if Host:
         self._host = Host
      
      if Sleep:
         try:
            self._sleep = int(Sleep)
         except ValueError as err:
            self.warn(err.message)
            self._sleep = 180
      else:
         self._sleep = 180

   def warn(self, fmt, *args):
      mesg = stdlib.sprintf(fmt, *args)
      self._log(message)
      warnings.warn(mesg)

   def error(self, fmt, *args):
      self.Close()
      message = stdlib.sprintf(fmt, *args)
      raise AppError(message)

   def Close(self):
      if self._svn:
         self._svn.Close()

      if self._svndir:
         try:
            os.stat(self._svndir)
         except OSError as err:
            pass
         else:
            shutil.rmtree(self._svndir)

   def __int__(self):
      return self._count

   def start(self):
      revision = 0
      flag = False
      if self._host:
         flag = True

      if hasattr(settings, 'SVN'):
         username = settings.SVN['USER']
         password = settings.SVN['PASS']
         url = settings.SVN['URL']
         try:
            self._svn = scm.svn(username = username, password = password)
            self._svn.Client()
         except scm.SCMError as err:
            self.error(err.message)

         self._svndir = settings.SVN['DIR'] + "/tmp/backups"

         try:
            shutil.rmtree(self._svndir)
         except OSError as err:
            pass

         try:
            os.chdir(settings.SVN['DIR'] + '/tmp')
            revision = self._svn.checkout("backups", url)
         except scm.SCMError as err:
            self.error(err.message)

         try:
            os.stat(self._svndir)
         except OSError as err:
            self.error("%s directory is not valid %s",
                  self._svndir, err.message)

      if not revison:
         self.error("SVN checkout cannot be at revision zero")

      devcount = 0
      devobjs = []

      for host in self._host:
         hostip = None
         try:
            hostip = ip.ip(host)
            if hostip.size() > 1:
               self.error("Host is defined as a network, cannot backup!")
         except exception.IPError as err:
            self.warn(err.message)
            continue

         try:
            device = Device.objects.get(snmp_target = Ipblock.objects.get(
               address = int(hostip)).id)
         except ObjectDoesNotExist as err:
            self.warn("Netdot does not know device with ip %s", hostip)
            continue
         else:
            devcount += 1
            devobjs.append(device)

      if flag and devcount == 0:
         self.error("No valid host defined!")

      if devcount == 0:
         devcount = Device.objects.count()
         devobjs = Device.objects.all()

      for ix in devcount:
         dev = device.device(self._q, self._sleep)
         devList.append(dev)
         dev.setDaemon(True)
         dev.start()

      for system in devobjs:
         if system.monitor_config and system.snmp_target:
            self._q.put(system)
         else:
            if not system.snmp_target:
               self.warn("%s does not have ip defined!", system)

      self._q.join()

      for dev in devList:
         config = dev.config
         if config:
            newconfig = "%s.%s.cfg" %(self._svndir, str(dev))
            shutil.move(config, newconfig)
            digest = stdlib.FileDigest(newconfig)
            date = time.strftime("%Y-%m-%d, %b %d %Y")
            svndict = self._svn.status(newconfig)[0]
            mesg = "checkin of %s on %s, digest: %s" %(newconfig, date, digest)
            if svndict.is_versioned:
               self.svn.CheckIn(newconfig, mesg)
            else:
               self.svn.CheckIn(newconfig, mesg, True)

            self._log("config file of %s: %s, digest: %s", dev, newconfig,
                  digest)

            if config:
               try:
                  os.unlink(config)
               except OSError:
                  pass

            devmodel = None
            try:
               devmodel = ConfigBackup.objects.get(device = int(dev))
            except ObjectDoesNotExist as err:
               self.warn("%s invalid database model for device: %s",
                     str(dev), err.message)
               continue
            else:
               devmodel.digest = digest
               devmodel.save()
               self._count += 1
         else:
            configerr = dev.error

def configbackups(HostList = [], Sleep = 240):
   retval = None
   evlog = log.StreamLog("configbackups")
   config = None
   try:
      config = ConfigBackup(HostList, Sleep)
      if config:
         config.backup()
   except AppError as err:
      evlog(err.message)
      retval = err.message

   return retval

